#include <iostream>
#include <memory>
#include <cstring>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>

enum class Status
{
    New,
    CONNECTED,
    CONNECTING,
    DISCONNECT,
    CLOSED
};

const static int gmax_revcon = 5;
const static int _grevcon_interval = 1;

class ClientConnect
{
public:
    ClientConnect()
    {
    }

    ClientConnect(std::string &serverip, uint16_t &serverport)
        : _ip(serverip),
          _port(serverport),
          _max_revcon(gmax_revcon),
          _revcon_interval(_grevcon_interval)
    {
    }

    void Connect()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            // 创建套接字失败，不玩了
            std::cout << "client creat sockfd error" << std::endl;
            return;
        }

        // 不需要bind

        // 连接
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        // 转成网络序列
        uint16_t serverport = htons(_port);
        addr.sin_port = serverport;
        addr.sin_family = AF_INET;
        inet_pton(AF_INET, _ip.c_str(), &addr.sin_addr);

        int n = connect(_sockfd, (struct sockaddr *)&addr, sizeof(addr));
        if (n < 0)
        {
            // 连接失败，重新连接
            _status = Status::DISCONNECT;
            std::cout << "连接失败,重新连接..." << std::endl;
            return;
        }

        // 连接成功
        _status = Status::CONNECTED;
    }

    void Process()
    {
        while (true)
        {
            std::cout << "Please Enter# ";
            std::string inbuffer;
            getline(std::cin, inbuffer);
            if (inbuffer.empty())
                continue;
            // 向服务器发送数据
            int n = send(_sockfd, inbuffer.c_str(), inbuffer.size(), 0);
            if (n > 0)
            {
                // 发送成功,进行读取
                char message[1024];
                memset(message,0,sizeof(message));
                int m = recv(_sockfd, message, sizeof(message), 0);
                if (m > 0)
                {
                    std::cout << message << std::endl;
                }
                else if (m == 0)
                {
                    // 服务器关闭，重新连接
                    std::cout << "连接失败,重新连接..." << std::endl;
                    _status = Status::DISCONNECT;
                    break;
                }
                else
                {
                    std::cout << "client write error, client closed" << std::endl;
                    _status = Status::CLOSED;
                    break;
                }
            }
            else
            {
                std::cout << "client write error, client closed" << std::endl;
                _status = Status::CLOSED;
            }
        }
    }

    void Revconnect()
    {
        _status = Status::CONNECTING;
        // 设置重连次数，不能一直重连吧
        int cnt = 0;
        while (cnt < _max_revcon)
        {
            Connect();
            if (_status == Status::CONNECTED)
            {
                // 重新连接成功
                return;
            }
            std::cout << "正在重连, 连接次数: " << cnt+1 << " 最大连接次数: " << _max_revcon << std::endl;
            cnt++;
            sleep(_revcon_interval);
        }
        // 超过重连次数,连接失败
        std::cout << "超过重连次数,连接失败" << std::endl;
        _status = Status::CLOSED;
    }

    void Closed()
    {
        if (_sockfd != -1)
        {
            ::close(_sockfd);
            _status = Status::CLOSED;
        }
    }

    Status GetStatus()
    {
        return _status;
    }

    ~ClientConnect()
    {
        Closed();
    }

private:
    std::string _ip;
    uint16_t _port;
    int _sockfd;
    Status _status;
    int _max_revcon;      // 最多重连次数
    int _revcon_interval; // 间隔
};

class TcpClient
{
public:
    TcpClient()
    {
    }

    TcpClient(std::string &serverip, uint16_t &serverport)
        : _client(serverip, serverport)
    {
    }

    void Excute()
    {
        while (true)
        {
            switch (_client.GetStatus())
            {
            case Status::New:
                _client.Connect();
                break;
            case Status::CONNECTED:
                _client.Process();
                break;
            case Status::DISCONNECT:
                _client.Revconnect();
                break;
            case Status::CLOSED:
                _client.Closed();
                return;
            }
        }
    }

    ~TcpClient()
    {
    }

private:
    ClientConnect _client;
};

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cout << "Usage: " << argv[0] << " serverip serverport" << std::endl;
        return 0;
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    TcpClient tc(serverip, serverport);
    tc.Excute();

    return 0;
}

// #include <iostream>
// #include <memory>
// #include <sys/types.h>
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <arpa/inet.h>
// #include <cstring>
// #include <string>
// #include <unistd.h>
// #include <functional>
// #include "Daemon.hpp"
// #include "Log.hpp"

// int main(int argc,char* argv[])
// {
//     if(argc != 3)
//     {
//         LOG(INFO,"./tcpclient serverip serverport\n");
//         exit(2);
//     }

//     //创建套接字
//     int sockfd = ::socket(AF_INET,SOCK_STREAM,0);
//     if(sockfd < 0)
//     {
//         LOG(FATAL,"client socket creat error\n");
//         exit(2);
//     }

//     std::string serverip = argv[1];
//     uint16_t serverport = std::stoi(argv[2]);

//     struct sockaddr_in server;
//     socklen_t len = sizeof(server);
//     memset(&server,0,len);
//     server.sin_family = AF_INET;
//     server.sin_port = htons(serverport);
//     ::inet_pton(AF_INET,serverip.c_str(),&server.sin_addr);

//     //connect
//     int n = ::connect(sockfd, (struct sockaddr *)&server, sizeof(server));
//     if (n < 0)
//     {
//         std::cerr << "connect socket error" << std::endl;
//         exit(2);
//     }

//     while(true)
//     {
//         std::string message;
//         std::cout << "Enter# ";
//         std::cin >> message;

//         //write
//         write(sockfd,message.c_str(),message.size());

//         //read
//         char buff[1024];
//         n = read(sockfd,buff,sizeof(buff)-1);
//         if(n > 0)
//         {
//             buff[n-1] = 0;
//             std::cout << buff<<std::endl;
//         }
//         else
//         {
//             break;
//         }
//     }
//     ::close(sockfd);

//     return 0;
// }
